package net.qiqbframework.config;

import lombok.extern.slf4j.Slf4j;
import net.qiqbframework.lifecycle.LifecycleHandler;
import net.qiqbframework.messaging.Message;
import net.qiqbframework.messaging.annotation.MessageHandlingMemberFactory;
import net.qiqbframework.messaging.correlation.CorrelationDataProvider;
import net.qiqbframework.messaging.monitoring.MessageMonitor;
import net.qiqbframework.serialization.Serializer;
import org.javers.core.Javers;
import org.javers.core.JaversBuilder;

import javax.annotation.Nonnull;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Supplier;

@Slf4j
public class DefaultConfigurationImpl implements Configuration {

    private final DefaultConfigurer configurer;

    public DefaultConfigurationImpl(DefaultConfigurer configurer) {
        this.configurer = configurer;
    }

    @Override
    public JaversBuilder aggregateJaversBuilder() {
        return configurer.aggregateJaversBuilder.get();
    }

    @Override
    public Javers aggregateJavers() {
        return configurer.aggregateJavers.get();
    }

    @Override
    public JaversBuilder poJaversBuilder() {
        return configurer.poJaversBuilder.get();
    }

    @Override
    public Javers poJavers() {
        return configurer.poJavers.get();
    }

    @Override
    public synchronized <T> T getComponent(@Nonnull Class<T> componentType, @Nonnull Supplier<T> defaultImpl) {
        Object component = configurer.components
                .computeIfAbsent(componentType, type ->
                        new ComponentHolder<>(this, componentType.getSimpleName(), c -> configurer.defaultComponent(componentType, c).orElseGet(defaultImpl))
                ).get();
        return componentType.cast(component);
    }

    @Override
    public <M extends Message<?>> MessageMonitor<? super M> messageMonitor(@Nonnull Class<?> componentType, @Nonnull String componentName) {
        return configurer.messageMonitorFactoryComponent.get().apply(componentType, componentName);
    }

    @Override
    public Serializer eventSerializer() {
        return configurer.eventSerializer.get();
    }

    @Override
    public Serializer messageSerializer() {
        return configurer.messageSerializer.get();
    }

    @Override
    public Serializer aggregateSerializer() {
        return configurer.aggregateSerializer.get();
    }

    @Override
    public void start() {
        configurer.invokeStartHandlers();
    }

    @Override
    public void shutdown() {
        configurer.invokeShutdownHandlers();
    }

    @Override
    public List<CorrelationDataProvider> correlationDataProviders() {
        return configurer.correlationProviders.get();
    }

    @Override
    public MessageHandlingMemberFactory handlerDefinition(Class<?> inspectedType) {
        return configurer.handlerDefinition.get().apply(inspectedType);
    }

    @Override
    public List<ModuleConfiguration> getModules() {
        return configurer.modules;
    }

    @Override
    public void onStart(int phase, LifecycleHandler startHandler) {
        if (isEarlierPhaseDuringStartUp(phase)) {
            log.info("A start handler is being registered for phase [{}] whilst phase [{}] is in progress. " + "Will run provided handler immediately instead.", phase, configurer.currentLifecyclePhase);
            startHandler.handle().join();
        }
        registerLifecycleHandler(configurer.startHandlers, phase, startHandler);
    }

    private boolean isEarlierPhaseDuringStartUp(int phase) {
        return configurer.lifecycleState == LifecycleState.STARTING_UP && configurer.currentLifecyclePhase != null && phase <= configurer.currentLifecyclePhase;
    }

    @Override
    public void onShutdown(int phase, LifecycleHandler shutdownHandler) {
        if (isEarlierPhaseDuringShutdown(phase)) {
            log.info("A shutdown handler is being registered for phase [{}] whilst phase [{}] is in progress. " + "Will run provided handler immediately instead.", phase, configurer.currentLifecyclePhase);
            shutdownHandler.handle().join();
        }
        registerLifecycleHandler(configurer.shutdownHandlers, phase, shutdownHandler);
    }

    private boolean isEarlierPhaseDuringShutdown(int phase) {
        return configurer.lifecycleState == LifecycleState.SHUTTING_DOWN && configurer.currentLifecyclePhase != null && phase >= configurer.currentLifecyclePhase;
    }

    private void registerLifecycleHandler(Map<Integer, List<LifecycleHandler>> lifecycleHandlers, int phase, LifecycleHandler lifecycleHandler) {
        lifecycleHandlers.compute(phase, (p, handlers) -> {
            if (handlers == null) {
                handlers = new CopyOnWriteArrayList<>();
            }
            handlers.add(lifecycleHandler);
            return handlers;
        });
    }
}
